import { observable, computed, action, toJS } from 'mobx';
//引入常量定义类
import CONSTS from '@/inits/consts';
//引入自己的工具包
import dateUtils from '@/utils/dateutils';
import JWT from '@/utils/jwtutil';
import NotificationUtils from '@/utils/notificationutils';
//引入初始化参数
import ITODO from '@/inits/itodo.json';
import axios from 'axios';
const LOGINDATA = CONSTS.LOGINDATA;
class AppStore implements IStoreType {
  /**************************** 数值定义  ********************************** */
  //1、这是定义的当前todo,供表单使用
  @observable thistodo = CONSTS.TODONULL;
  //2、决定表单按钮 是渲染为create表单还是修改表单
  @observable nowstate = CONSTS.FROMTYPE.CREATE;
  /* 3、此处初始化一个使用说明,当本地变量没有时候 将会使用该json数据(使用说明书)  如果有数本地据将使用本地数据
        id  更新时间(对象数组{更新时间,修改的事件状态})  事件内容   事件状态  开始时间   截止日期    (之后可加个是否收藏(重要)标识)
  */
  @observable todos = [ITODO];
  //当前处于的显示页面
  @observable isShow = CONSTS.ISSHOW.MY;
  //是否显示隐藏填写框
  @observable visible = false;
  //搜索关键字与搜索标记
  @observable srarch = '';
  //当为true时,列表渲染将会是搜索内容的渲染
  @observable isSrarch = false;

  //绑定是否批量删除
  @observable batchdel = false;
  //绑定选中的li的id
  @observable batchdels = [''];
  //绑定是否游客进入
  @observable active = false;

  /*************************  方法动作  ************************ */
  //1、初始化赋值todos
  /* 思路Mark:当游客进入,我将存取localage的todos 当用户登录时我将直接操作数据库不修改localage,即可达到游客与登录用户兼容 */
  @action initTodos() {
    if (localStorage.getItem('dbtoken')) {
      let jwt: any = {};
      jwt = JWT.verify(localStorage.getItem('dbtoken'), CONSTS.JWTCONFIG.KEY);
      console.log(
        jwt?.decode?.sign?.todos.trim().length,
        '---------------------------------------',
      );
      if (!jwt.err && jwt?.decode?.sign?.todos.trim().length > 0) {
        this.todos = JSON.parse(
          JWT.verify(jwt?.decode?.sign?.todos, CONSTS.JWTCONFIG.KEY)?.decode
            ?.todos,
        );
      }
      //  if (!jwt.err) this.todos = JSON.parse(jwt.decode.todos); //进行解密,当揭秘成功时 赋值解密成功后的值
    } else {
      let jwt: any = {};
      if (localStorage.getItem('todos') && !this.active) {
        //判断本地是否有变量
        jwt = JWT.verify(localStorage.getItem('todos'), CONSTS.JWTCONFIG.KEY);
        if (!jwt.err) this.todos = JSON.parse(jwt.decode.todos); //进行解密,当揭秘成功时 赋值解密成功后的值
      }
    }
    //如果有今天到期的待办事件,将选出最近的那件进行桌面通知
    if (this.getDay.newArr.length != 0)
      NotificationUtils.notification(toJS(this.getDay.newArr[0]).content);
  }
  //2、修改表单状态  是渲染为create表单还是修改表单
  @action handleChangeNowstate(state: string) {
    this.nowstate = state;
  }
  //3、修改显示与隐藏
  @action changeVisible() {
    this.visible = !this.visible;
  }
  //4、修改当前显示状态
  @action hadleNowstate(params: string) {
    this.nowstate = params;
  }
  //5、传入todo进行表单渲染
  @action handleshowRender(todo: any) {
    this.thistodo = todo;
  }
  //6、点击修改显示的页面 已处理未处理已过期已删除
  @action handleIsShow(isshow: string) {
    this.isShow = isshow;
  }
  /**增删改函数 */
  //7、往todos添加todo,同时直接排序 而后存入本地变量, 此处也是通用函数,
  @action addTodos(params: todoType) {
    this.todos.unshift(params); //unshift原本是打算按照插入时间排序,但是考虑应该按照到期时间进行排序更合理,于是进行排序
    this.todosSort(); //增加修改后应该先进行排序再存入
    this.jwtSign();
  }
  //8、修改todos中的某个todo,同时进行排序 ,这里已经在表单提交中修改好了update信息,所以直接赋值
  @action updateTodos(params: todoType) {
    for (let i = 0; i < this.todos.length; i++) {
      if (this.todos[i].id == params.id) this.todos[i] = params;
    }
    this.todosSort();
    this.jwtSign();
  }
  //9、修改todos中某个todo的state状态,传入的参数应为想要修改成的状态,并且记录更新时间与更新操作
  /* 此处踩坑:原本做法是删除数组中某一项再进行添加
    但修改更新不应该修改数组长度,应该直接修改某个值,于是修改成这样 */
  @action updateTodoState(id: string, state: string) {
    this.todos.map((item) => {
      if (toJS(item).id == id) {
        item.updatetime.unshift({
          updatetime: new Date().getTime().toString(),
          updatestate: state,
        }); //上下顺序不可调换,先将还原操作记录,
        if (state == CONSTS.ISSHOW.COLLECT) item.collect = !item.collect;
        //如果传入的state参数是已收藏状态,我将修改收藏状态
        else if (state == CONSTS.STATE.REDUCTION)
          item.state = CONSTS.STATE.UNDONE;
        //如果状态为回收,改为未完成,即移出回收站
        else item.state = state; //如果不是以上两个特例,传入什么状态就直接赋值进去
      }
    });
    this.jwtSign();
  }

  //10、 清空回收站
  @action handleClearRecyle() {
    //使用过滤器筛选出不是回收站的todo,然后存入,这里不需要再排序,因为操作的数据删除后仍是排序好的状态
    this.todos = toJS(this.todos).filter(
      (item) => item.state != CONSTS.STATE.DELETE,
    );
    this.jwtSign();
    alert('回收站已经清空');
  }

  //11、搜索内容框内容绑定
  @action handleSrarch(value: string) {
    this.srarch = value;
  }
  //12.加密与保存方法,这个方法并不需要@action,所以我直接定义
  jwtSign() {
    let jwt_token = JWT.sign(
      { todos: JSON.stringify(this.todos) },
      CONSTS.JWTCONFIG.KEY,
      CONSTS.JWTCONFIG.EXPIRES,
    ); //本地数据加密处理,并且设置数据过期时间

    let user = JWT.verify(localStorage.getItem('dbtoken'), LOGINDATA.KEY); //如果没有localStorage的值,解密也是失败
    if (user.err) {
      localStorage.setItem('todos', jwt_token);
      return;
    }
    user = user?.decode?.sign;
    user.todos = jwt_token;
    console.log(user);
    axios({
      method: 'put',
      url: 'http://localhost:53000/users/' + user?.id,
      data: { ...user },
    }).then(
      (v) => {
        console.log('更新数据成功');
      },
      (e) => {
        console.log(e);
      },
    );
  }
  //13.排序方法,修改日期或者增加todos后需要调用
  todosSort() {
    this.todos = toJS(this.todos).sort(
      (a, b) => Number(a.deadline) - Number(b.deadline),
    ); //进行排序 这里需要进行tojs,否则无法排序
  }
  //14.表单绑定value以及时间修改
  @action onChangefromTodoContent(value: string) {
    this.thistodo.content = value;
  }
  @action onChangefromTodoTime(start: any, deadline: any) {
    this.thistodo.start = start;
    this.thistodo.deadline = deadline;
  }
  //15、修改批量删除状态  是渲染为create表单还是修改表单
  @action handleBatchdel(batchdel?: boolean) {
    //此处因为传入的是布尔值 false与undefined会被表达式混淆 所以不用 this.batchdel =batchdel || !this.batchdel写法
    if (batchdel == undefined) this.batchdel = !this.batchdel;
    else this.batchdel = batchdel;
  }
  //16、添加待选数组,重复添加即取消选中,删除
  /*  这里有一个大坑 直接push并没有效果,
       错误的地方: “使用” observable 但没有访问它的任何属性 问题详见: https://cn.mobx.js.org/best/react.html 
    */
  @action addBatchdels(id: string) {
    if (this.batchdels.indexOf(id) > 0)
      this.batchdels = this.batchdels.filter((item) => item !== id);
    else {
      this.batchdels.push(id);
      this.batchdels = toJS(this.batchdels).sort(); //疑惑: 这里我push后进行排序强行修改,可有什么更好的方法,先记于笔记上
    }
  }
  //17、清空待选栏并且移入回收站
  @action clearBatchdels() {
    this.todos.map((item, index) => {
      this.batchdels.map((value) => {
        if (toJS(item).id == value) {
          //批量时候也需要记录更新状态
          item.updatetime.unshift({
            updatetime: new Date().getTime().toString(),
            updatestate: this.todos[index].state,
          });
          this.todos[index].state = CONSTS.STATE.DELETE;
        }
      });
    });
    this.jwtSign();
    // alert("完成批量操作")
  }
  //18、 清空待选数组但不移入回收站 后面补的功能 因该叫做clear的
  @action clearBatchdelsNoDel() {
    this.batchdels = [''];
  }
  //19、根据id进行是否收藏查询 返回boolean
  @action getCollectByID(id: string) {
    let falg = false;
    this.filterTodos.collectArr.map((item) => {
      if (id == item.id) {
        falg = true;
      }
    });
    return falg;
  }

  /*****************************计算方法********************************** */
  //1、计算返回不同剩余天数的数组==> 一周,未来,过期
  @computed get getDay() {
    let weekArr: todoType[] = [];
    let futerArr: todoType[] = [];
    let pastArr: todoType[] = [];
    let newArr: todoType[] = [];
    const time = new Date().getTime();
    let todos = this.todos;
    switch (
      this.isShow //先进行处理未处理筛选操作
    ) {
      case CONSTS.ISSHOW.DONE:
        todos = this.filterTodos.doneArr;
        break;
      case CONSTS.ISSHOW.UNDONE:
        todos = this.filterTodos.undoneArr;
        break;
      case CONSTS.ISSHOW.RECYCLE:
        todos = this.filterTodos.recycleArr;
        break;
      case CONSTS.ISSHOW.COLLECT:
        todos = this.filterTodos.collectArr;
        break;
      default:
        todos = this.filterTodos.todosArr;
        break;
    }
    //2、筛选一周前，未来，过期数字
    todos.map((item) => {
      if (
        dateUtils.intervalTime(time, item.deadline) <= 7 &&
        dateUtils.intervalTime(time, item.deadline) >= 0
      ) {
        weekArr.push(item);
        if (dateUtils.intervalTime(time, item.deadline) <= 1) newArr.push(item); //用来筛选当天任务,坐在初始化桌面通知
      } else if (dateUtils.intervalTime(time, item.deadline) > 7)
        futerArr.push(item);
      else pastArr.push(item);
    });
    return { weekArr, futerArr, pastArr, newArr };
  }
  //3、计算筛选todos数组， 完成，未完成，回收站，未删除 已收藏
  @computed get filterTodos() {
    let doneArr: todoType[] = [];
    let undoneArr: todoType[] = [];
    let recycleArr: todoType[] = [];
    let todosArr: todoType[] = [];
    let collectArr: todoType[] = [];
    this.todos.map((item) => {
      if (item.state != CONSTS.STATE.DELETE) todosArr.push(item);
      if (item.collect && item.state != CONSTS.STATE.DELETE)
        collectArr.push(item);
      switch (item.state) {
        case CONSTS.STATE.DELETE:
          recycleArr.push(item);
          break;
        case CONSTS.STATE.UNDONE:
          undoneArr.push(item);
          break;
        case CONSTS.STATE.DONE:
          doneArr.push(item);
          break;
      }
    });
    return { doneArr, undoneArr, recycleArr, todosArr, collectArr };
  }

  //4、搜索后的结果数组
  @computed get searchResult() {
    let srarch = this.srarch.trim(); // 去除搜索框前后空格 并且通过时间与内容筛选
    let weekArr = toJS(
      this.getDay.weekArr.filter(
        (item) =>
          item.content.includes(srarch) ||
          dateUtils.getLocalTime(item.deadline).includes(srarch),
      ),
    );
    let futerArr = toJS(
      this.getDay.futerArr.filter(
        (item) =>
          item.content.includes(srarch) ||
          dateUtils.getLocalTime(item.deadline).includes(srarch),
      ),
    );
    let pastArr = toJS(
      this.getDay.pastArr.filter(
        (item) =>
          item.content.includes(srarch) ||
          dateUtils.getLocalTime(item.deadline).includes(srarch),
      ),
    );
    return { weekArr, futerArr, pastArr };
  }
  //5、判断当前是搜索还是正常
  @computed get getArrObj() {
    let weekArr: todoType[] = [];
    let futerArr: todoType[] = [];
    let pastArr: todoType[] = [];
    let arr = { weekArr, futerArr, pastArr };
    this.isSrarch ? (arr = this.getDay) : (arr = this.searchResult);
    return arr;
  }
}

//在这里实例化后再导出,方便后面使用
let store = new AppStore();
export default store;
